home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / FastReadStream.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-20  |  6.8 KB  |  251 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include <windows.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <io.h>
  22. #include <crtdbg.h>
  23.  
  24. #include "Error.h"
  25. #include "FastReadStream.h"
  26.  
  27. class FastReadStreamHeader {
  28. public:
  29.     __int64 i64BlockNo;
  30.     long fAccessedBits;
  31.     long lBytes;
  32.     long lAge;
  33.     long lHistoryVal;
  34. };
  35.  
  36. FastReadStream::FastReadStream(HANDLE hFile, long lBlockCount, long lBlockSize) {
  37.     this->hFile            = hFile;
  38.     this->iFile            = -1;
  39.  
  40.     _Init(lBlockCount, lBlockSize);
  41. }
  42.  
  43. FastReadStream::FastReadStream(int iFile, long lBlockCount, long lBlockSize) {
  44.     this->hFile            = INVALID_HANDLE_VALUE;
  45.     this->iFile            = iFile;
  46.     _Init(lBlockCount, lBlockSize);
  47. }
  48.  
  49. void FastReadStream::_Init(long lBlockCount, long lBlockSize) {
  50.     this->lBlockCount    = lBlockCount;
  51.     this->lBlockSize    = (lBlockSize + 4095) & -4096;
  52.     this->pHeaders        = new FastReadStreamHeader[this->lBlockCount];
  53.     this->pBuffer        = VirtualAlloc(NULL, this->lBlockCount * this->lBlockSize, MEM_COMMIT, PAGE_READWRITE);
  54.  
  55.     if (!this->pHeaders || !this->pBuffer) {
  56.         delete this->pHeaders;
  57.         if (this->pBuffer) VirtualFree(this->pBuffer, 0, MEM_RELEASE);
  58.  
  59.         this->pHeaders = NULL;
  60.         this->pBuffer = NULL;
  61.     } else {
  62.         Flush();
  63.     }
  64.  
  65.     lHistory            = 0;
  66. }
  67.  
  68. bool FastReadStream::Ready() {
  69.     return pHeaders && pBuffer;
  70. }
  71.  
  72. FastReadStream::~FastReadStream() {
  73.     delete pHeaders;
  74.     if (pBuffer) VirtualFree(pBuffer, 0, MEM_RELEASE);
  75. }
  76.  
  77. ///////////////////////////////////////////////////////////////////////////
  78.  
  79. #pragma function(memcpy)
  80.  
  81. long FastReadStream::Read(int stream, __int64 i64Pos, void *pDest, long lBytes) {
  82.     long lOffset, lActual = 0, lToCopy;
  83.     __int64 i64BlockNo;
  84.     char *pBuffer2 = (char *)pDest;
  85.     int iCacheBlock;
  86.  
  87.     // First block number and offset...
  88.  
  89.     i64BlockNo = i64Pos / lBlockSize;
  90.     lOffset = i64Pos % lBlockSize;
  91.  
  92. //    _RPT3(0,"Read request: %ld bytes, pos %I64x, first block %I64d\n", lBytes, i64Pos, i64BlockNo);
  93.  
  94.     while(lBytes) {
  95.         long lInBlock;
  96.  
  97.         lToCopy = lBlockSize - lOffset;
  98.         if (lToCopy > lBytes) lToCopy = lBytes;
  99.  
  100.         iCacheBlock = _Commit(stream, i64BlockNo);
  101.         lInBlock = pHeaders[iCacheBlock].lBytes - lOffset;
  102.  
  103. //        _RPT4(0,"(%ld) Reading %ld from cache block %d, offset %ld\n", stream, lToCopy, iCacheBlock, lOffset);
  104.  
  105.         if (lInBlock < lToCopy) {
  106.             if (lInBlock > 0) {
  107.                 memcpy(pBuffer2, (char *)pBuffer + iCacheBlock * lBlockSize + lOffset, lInBlock);
  108.  
  109.                 lActual += lInBlock;
  110.             }
  111.  
  112.             break;
  113.         } else
  114.             memcpy(pBuffer2, (char *)pBuffer + iCacheBlock * lBlockSize + lOffset, lToCopy);
  115.  
  116.         pBuffer2 += lToCopy;
  117.         lBytes -= lToCopy;
  118.         lActual += lToCopy;
  119.         ++i64BlockNo;
  120.         lOffset = 0;
  121.     }
  122.  
  123.     return lActual;
  124. }
  125.  
  126. void FastReadStream::Flush() {
  127.     for(int i=0; i<lBlockCount; i++) {
  128.         pHeaders[i].i64BlockNo = -1;
  129.         pHeaders[i].fAccessedBits = 0;
  130.         pHeaders[i].lHistoryVal = 0;
  131.     }
  132.  
  133.     lHistory = 0;
  134. }
  135.  
  136. ///////////////////////////////////////////////////////////////////////////
  137.  
  138. int FastReadStream::_PickVictim(int stream) {
  139.     int i;
  140.     int iLoneBlock = -1;
  141.     long fStreamEncounteredBits=0, fStreamNotLoneBits=0;
  142.     int iOurLowest=-1, iGlobalLowest=-1, iPreferred=-1;
  143.     long fStreamMask = 1L<<stream;
  144.  
  145.     // Look for an unused block.
  146.  
  147.     for(i=0; i<lBlockCount; i++)
  148.         if (pHeaders[i].i64BlockNo == -1)
  149.             return i;
  150.  
  151.     // Compile a list of streams with lone blocks.  These can't be replaced.
  152.     // Look for our lone block.
  153.  
  154.     for(i=0; i<lBlockCount; i++) {
  155.         // Encountered bits -> NotLone bits
  156.  
  157.         fStreamNotLoneBits |= fStreamEncounteredBits & pHeaders[i].fAccessedBits;
  158.         fStreamEncounteredBits |= pHeaders[i].fAccessedBits;
  159.     }
  160.  
  161.     // Look at the histories, and choose a few candidates.
  162.  
  163.     for(i=0; i<lBlockCount; i++) {
  164.         long lThisHistory = lHistory - pHeaders[i].lHistoryVal;
  165.  
  166.         if (lThisHistory<0) lThisHistory = 0x7FFFFFFF;
  167.  
  168.         pHeaders[i].lAge = lThisHistory;
  169.  
  170.         // Our oldest block
  171.  
  172.         if (pHeaders[i].fAccessedBits & fStreamMask)
  173.             if (iOurLowest<0 || lThisHistory > pHeaders[iOurLowest].lAge)
  174.                 iOurLowest = i;
  175.  
  176.         // Global oldest block
  177.  
  178.         if (iGlobalLowest<0 || lThisHistory > pHeaders[iGlobalLowest].lAge)
  179.             iGlobalLowest = i;
  180.  
  181.         // Preferred lowest block
  182.  
  183.         if (pHeaders[i].fAccessedBits & fStreamMask
  184.             && !(pHeaders[i].fAccessedBits & ~fStreamNotLoneBits))
  185.             if (iPreferred<0 || lThisHistory > pHeaders[iPreferred].lAge)
  186.                 iPreferred = i;
  187.     }
  188.  
  189.     return iPreferred>=0 ? iPreferred : iOurLowest>=0 ? iOurLowest : iGlobalLowest;
  190. }
  191.  
  192. int FastReadStream::_Commit(int stream, __int64 i64BlockNo) {
  193.     int iCacheBlock;
  194.     int i;
  195.  
  196.     // Already have the block?
  197.  
  198.     for(i=0; i<lBlockCount; i++)
  199.         if (pHeaders[i].i64BlockNo == i64BlockNo) {
  200.             pHeaders[i].fAccessedBits |= 1L<<stream;
  201. //            _RPT1(0,"Commit(%I64d): cache hit\n", i64BlockNo);
  202.             return i;
  203.         }
  204.  
  205.     // Pick a replacement candidate.
  206.  
  207.     iCacheBlock = _PickVictim(stream);
  208.  
  209.     // Replace it.
  210.  
  211.     try {
  212.         ++lHistory;
  213.  
  214.         _RPT2(0,"Commit(%I64d): cache miss (stream %d)\n", i64BlockNo, stream);
  215.         if (iFile >= 0) {
  216.             int iActual;
  217.  
  218.             if (-1 == _lseeki64(iFile, i64BlockNo * lBlockSize, SEEK_SET))
  219.                 throw MyError("FastRead seek error: %s.", strerror(errno));
  220.  
  221.             iActual = _read(iFile, (char *)pBuffer + iCacheBlock * lBlockSize, lBlockSize);
  222.  
  223.             if (iActual < 0)
  224.                 throw MyError("FastRead read error: %s.", strerror(errno));
  225.  
  226.             pHeaders[iCacheBlock].lBytes = iActual;
  227.  
  228.         } else {
  229.             LONG lLow = (LONG)i64BlockNo*lBlockSize;
  230.             LONG lHigh = (LONG)((i64BlockNo*lBlockSize) >> 32);
  231.             DWORD err, dwActual;
  232.  
  233.             if (0xFFFFFFFF == SetFilePointer(hFile, lLow, &lHigh, FILE_BEGIN))
  234.                 if ((err = GetLastError()) != NO_ERROR)
  235.                     throw MyWin32Error("FastRead seek error: %%s", GetLastError());
  236.  
  237.             if (!ReadFile(hFile, (char *)pBuffer + iCacheBlock * lBlockSize, lBlockSize, &dwActual, NULL))
  238.                 throw MyWin32Error("FastRead read error: %%s", GetLastError());
  239.  
  240.             pHeaders[iCacheBlock].lBytes = dwActual;
  241.         }
  242.         pHeaders[iCacheBlock].i64BlockNo = i64BlockNo;
  243.         pHeaders[iCacheBlock].fAccessedBits = 1L<<stream;
  244.         pHeaders[iCacheBlock].lHistoryVal = lHistory;
  245.     } catch(...) {
  246.         pHeaders[iCacheBlock].i64BlockNo = -1;
  247.         pHeaders[iCacheBlock].fAccessedBits = 0;
  248.     }
  249.  
  250.     return iCacheBlock;
  251. }